package com.example.user_manage.config;


import com.example.user_manage.Component.MyAccessDeniedHandler;
import com.example.user_manage.Component.MyAuthenticationFailureHandler;
import com.example.user_manage.Component.MyAuthenticationSuccessHandler;
import com.example.user_manage.Component.MyLogoutHandler;
import com.example.user_manage.constants.UrlConstants;
import com.example.user_manage.filter.TokenFilter;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig {
    public static final String LOGOUT = "/logout";
    /**
     * 身份验证成功的处理器
     */
    private final MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    /**
     * 身份验证失败处理器
     */
    private final MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    /**
     * Token过滤器，验证token是否正确
     */
    private final TokenFilter tokenFilter;
    /**
     * 自定义访问拒绝处理器
     */
    private final MyAccessDeniedHandler myAccessDeniedHandler;
    /**
     * 自定义退出登录处理器
     */
    @Autowired
    private final MyLogoutHandler myLogoutHandler;

    /**
     * 密码加密器
     * @return 密码加密器对象
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        //因为日数据库里的密码没有加密，所以不加密密码
        //return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    /**
     * Security过滤器链
     * @param http Security过滤器对http请求的处理配置对象
     * @return Security过滤器链实现对象
     * @throws Exception 所有异常类型
     */
    @Bean
    public SecurityFilterChain securityFilterChain(
            HttpSecurity http) throws Exception {
        return http
                .formLogin((formLogin) ->{
                    //指定登录界面url
                    formLogin
                            //指定处理登录请求的url
                            .loginProcessingUrl("/toLogin").permitAll()
                            //添加登录成功路成功处理器
                            .successHandler(myAuthenticationSuccessHandler)
                            //添加登录失败处理器
                            .failureHandler(myAuthenticationFailureHandler)
                            ;
                })
                //退出登录相关配置
                .logout((logout) ->{
                    //指定退出的提交地址
                    logout.logoutUrl(LOGOUT)
                            //指定登录成功使用自定义的myLogoutHandler作为处理器
                            .logoutSuccessHandler(myLogoutHandler);
                })
                //http请求认证配置
                .authorizeHttpRequests((authorizeHttpRequests)
                        -> authorizeHttpRequests
                        //该路径的访问不需要认证
                        .requestMatchers(HttpMethod.POST, UrlConstants.WHITE_LIST).permitAll()
                        //其他所有请求都需要身份认证
                        .anyRequest().authenticated())
                //关闭csrf保护
                .csrf(AbstractHttpConfigurer::disable)
                //异常处理
                .exceptionHandling((exceptionHandling)->{
                    //添加访问拒绝处理器
                    exceptionHandling.accessDeniedHandler(myAccessDeniedHandler);
                })
                //注册跨域配置
                .cors((cors)
                        ->cors.configurationSource(corsConfigurationSource()))
                //设置session创造方式为无状态
                .sessionManagement((sessionManagement)
                        -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //添加token过滤器在退出过滤器之后的位置
                .addFilterBefore(tokenFilter, LogoutFilter.class)
                .build();
    }

    /**
     * 跨域配置
     * @return 跨域配置源
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        //url路径相关的跨域配置
        UrlBasedCorsConfigurationSource source
                = new UrlBasedCorsConfigurationSource();
        //创就按跨域配置对象
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");//允许任何来源
        config.addAllowedHeader("*");//允许任何请求头（后续会带jwt）
        config.addAllowedMethod("*");//允许任何请求方法（get、post、put、delete）
        //注册跨域配置
        source.registerCorsConfiguration("/**", config);
        return source;
    }

}
